package com.example.learningnote;

import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;

public class BitmapFactory {

    private static RenderScript sRsContext;
    private static final Object sLockForRsContext = new Object();

    public static Bitmap fastBlur(Context context, Bitmap bmpIn, int radius) {
        Bitmap bmpOut;
        bmpOut = copyToEmpty(bmpIn);
        fastBlur(context, bmpIn, bmpOut, radius);
        return bmpOut;
    }

    private static Bitmap fastBlur(Context context, Bitmap srcBmp, Bitmap dstBmp, int radius) {
        if (srcBmp == null) {
            return null;
        }
        if (dstBmp == null || srcBmp.getWidth() != dstBmp.getWidth() || srcBmp.getHeight() != dstBmp.getHeight()) {
            dstBmp = copyToEmpty(srcBmp);
        }

        fastblur_v17(context, srcBmp, dstBmp, radius);

        return dstBmp;
    }

    private static Bitmap fastblur_v17(Context context, Bitmap srcBmp, Bitmap dstBmp, int radius) {
        int scale = 1;
        while (radius > 25) {
            // ScriptIntrinsicBlur only supports radius from 0 to 25,
            // if the radius above 25, scale the image to smaller.
            scale *= 2;
            radius /= 2;
        }
        Bitmap srcScaledBmp;
        int w = srcBmp.getWidth();
        int h = srcBmp.getHeight();
        if (scale == 1) {
            srcScaledBmp = srcBmp;
        } else {
            srcScaledBmp = scaleBitmap(srcBmp, w / scale, h / scale);
        }

        if (context == null || context.getApplicationContext() == null) {
            context = new ContextWrapper(context) {
                @Override
                public Context getApplicationContext() {
                    return this;
                }
            };
        }
        synchronized (sLockForRsContext) {
            if (sRsContext == null) {
                sRsContext = RenderScript.create(context);
            }

            Bitmap dstScaledBmp;
            if (scale == 1) {
                dstScaledBmp = dstBmp;
            } else {
                dstScaledBmp = srcScaledBmp;
            }

            // ScriptIntrinsicBlur不能处理截屏得到的Bitmap，其row bytes与普通的Bitmap不一致，因此做此处理
            if (srcScaledBmp.getRowBytes() != dstScaledBmp.getRowBytes()) {
                srcScaledBmp = srcScaledBmp.copy(Bitmap.Config.ARGB_8888, true);
            }

            Allocation input = Allocation.createFromBitmap(sRsContext, srcScaledBmp);
            Allocation output = Allocation.createTyped(sRsContext, input.getType());
            ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(sRsContext, Element.U8_4(sRsContext));
            script.setRadius(radius);
            script.setInput(input);
            script.forEach(output);

            output.copyTo(dstScaledBmp);

            if (dstScaledBmp != dstBmp) {
                scaleBitmap(dstScaledBmp, dstBmp);
            }
            if (srcScaledBmp != srcBmp) {
                srcScaledBmp.recycle();
            }
            if (dstScaledBmp != dstBmp) {
                dstScaledBmp.recycle();
            }
        }

        return dstBmp;
    }

    private static Bitmap copyToEmpty(Bitmap src) {
        Bitmap.Config config = Bitmap.Config.ARGB_8888;
        if (src.getConfig() != null) {
            config = src.getConfig();
        }
        return Bitmap.createBitmap(src.getWidth(), src.getHeight(), config);
    }

    private static Bitmap scaleBitmap(Bitmap srcBmp, int destWidth, int destHeight) {
        if (srcBmp == null) {
            return null;
        }

        Bitmap destBmp;
        if (srcBmp.getWidth() == destWidth && srcBmp.getHeight() == destHeight) {
            destBmp = srcBmp;
        } else {
            Bitmap.Config config = Bitmap.Config.ARGB_8888;
            if (srcBmp.getConfig() != null) {
                config = srcBmp.getConfig();
            }
            destBmp = Bitmap.createBitmap(destWidth, destHeight, config);

            scaleBitmap(srcBmp, destBmp);
        }

        return destBmp;
    }

    private static Bitmap scaleBitmap(Bitmap srcBmp, Bitmap dstBmp) {
        if (srcBmp == null || dstBmp == null) {
            return null;
        }
        if (srcBmp.getWidth() == dstBmp.getWidth() && srcBmp.getHeight() == dstBmp.getHeight()) {
            dstBmp = srcBmp;
        } else {
            Canvas canvas = new Canvas(dstBmp);
            canvas.drawARGB(0, 0, 0, 0);

            final Paint paint = new Paint();
            paint.setFilterBitmap(true);
            paint.setAntiAlias(true);
            paint.setDither(true);

            canvas.drawBitmap(srcBmp, new Rect(0, 0, srcBmp.getWidth(), srcBmp.getHeight()),
                    new Rect(0, 0, dstBmp.getWidth(), dstBmp.getHeight()), paint);
        }
        return dstBmp;
    }
}
